{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e954495e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "\n",
    "class Inception(nn.Module):\n",
    "    def __init__(self, input_channels, n1x1, n3x3_reduce, n3x3, n5x5_reduce, n5x5, pool_proj):\n",
    "        super().__init__()\n",
    "\n",
    "        #1x1conv branch\n",
    "        self.b1 = nn.Sequential(\n",
    "            nn.Conv2d(input_channels, n1x1, kernel_size=1),\n",
    "            nn.BatchNorm2d(n1x1),\n",
    "            nn.ReLU(inplace=True)\n",
    "        )\n",
    "\n",
    "        #1x1conv -> 3x3conv branch\n",
    "        self.b2 = nn.Sequential(\n",
    "            nn.Conv2d(input_channels, n3x3_reduce, kernel_size=1),\n",
    "            nn.BatchNorm2d(n3x3_reduce),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(n3x3_reduce, n3x3, kernel_size=3, padding=1),\n",
    "            nn.BatchNorm2d(n3x3),\n",
    "            nn.ReLU(inplace=True)\n",
    "        )\n",
    "\n",
    "        #1x1conv -> 5x5conv branch\n",
    "        #we use 2 3x3 conv filters stacked instead\n",
    "        #of 1 5x5 filters to obtain the same receptive\n",
    "        #field with fewer parameters\n",
    "        self.b3 = nn.Sequential(\n",
    "            nn.Conv2d(input_channels, n5x5_reduce, kernel_size=1),\n",
    "            nn.BatchNorm2d(n5x5_reduce),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(n5x5_reduce, n5x5, kernel_size=3, padding=1),\n",
    "            nn.BatchNorm2d(n5x5, n5x5),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1),\n",
    "            nn.BatchNorm2d(n5x5),\n",
    "            nn.ReLU(inplace=True)\n",
    "        )\n",
    "\n",
    "        #3x3pooling -> 1x1conv\n",
    "        #same conv\n",
    "        self.b4 = nn.Sequential(\n",
    "            nn.MaxPool2d(3, stride=1, padding=1),\n",
    "            nn.Conv2d(input_channels, pool_proj, kernel_size=1),\n",
    "            nn.BatchNorm2d(pool_proj),\n",
    "            nn.ReLU(inplace=True)\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        return torch.cat([self.b1(x), self.b2(x), self.b3(x), self.b4(x)], dim=1)\n",
    "\n",
    "\n",
    "class GoogleNet(nn.Module):\n",
    "\n",
    "    def __init__(self, num_class=100):\n",
    "        super().__init__()\n",
    "        self.prelayer = nn.Sequential(\n",
    "            nn.Conv2d(3, 64, kernel_size=3, padding=1, bias=False),\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(64, 64, kernel_size=3, padding=1, bias=False),\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(64, 192, kernel_size=3, padding=1, bias=False),\n",
    "            nn.BatchNorm2d(192),\n",
    "            nn.ReLU(inplace=True),\n",
    "        )\n",
    "\n",
    "        #although we only use 1 conv layer as prelayer,\n",
    "        #we still use name a3, b3.......\n",
    "        self.a3 = Inception(192, 64, 96, 128, 16, 32, 32)\n",
    "        self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)\n",
    "\n",
    "        ##\"\"\"In general, an Inception network is a network consisting of\n",
    "        ##modules of the above type stacked upon each other, with occasional\n",
    "        ##max-pooling layers with stride 2 to halve the resolution of the\n",
    "        ##grid\"\"\"\n",
    "        self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)\n",
    "\n",
    "        self.a4 = Inception(480, 192, 96, 208, 16, 48, 64)\n",
    "        self.b4 = Inception(512, 160, 112, 224, 24, 64, 64)\n",
    "        self.c4 = Inception(512, 128, 128, 256, 24, 64, 64)\n",
    "        self.d4 = Inception(512, 112, 144, 288, 32, 64, 64)\n",
    "        self.e4 = Inception(528, 256, 160, 320, 32, 128, 128)\n",
    "\n",
    "        self.a5 = Inception(832, 256, 160, 320, 32, 128, 128)\n",
    "        self.b5 = Inception(832, 384, 192, 384, 48, 128, 128)\n",
    "\n",
    "        #input feature size: 8*8*1024\n",
    "        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n",
    "        self.dropout = nn.Dropout2d(p=0.4)\n",
    "        self.linear = nn.Linear(1024, num_class)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.prelayer(x)\n",
    "        x = self.maxpool(x)\n",
    "        x = self.a3(x)\n",
    "        x = self.b3(x)\n",
    "\n",
    "        x = self.maxpool(x)\n",
    "\n",
    "        x = self.a4(x)\n",
    "        x = self.b4(x)\n",
    "        x = self.c4(x)\n",
    "        x = self.d4(x)\n",
    "        x = self.e4(x)\n",
    "\n",
    "        x = self.maxpool(x)\n",
    "\n",
    "        x = self.a5(x)\n",
    "        x = self.b5(x)\n",
    "\n",
    "        #\"\"\"It was found that a move from fully connected layers to\n",
    "        #average pooling improved the top-1 accuracy by about 0.6%,\n",
    "        #however the use of dropout remained essential even after\n",
    "        #removing the fully connected layers.\"\"\"\n",
    "        x = self.avgpool(x)\n",
    "        x = self.dropout(x)\n",
    "        x = x.view(x.size()[0], -1)\n",
    "        x = self.linear(x)\n",
    "\n",
    "        return x\n",
    "\n",
    "def googlenet():\n",
    "    return GoogleNet()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
